'''
Created on Jan 20, 2013

@author: PoZ
'''


import game

import math

class Model(object):
    """
    Model is a facade to manager all Sprite of a single Entity
    It will pick the correct sprite based on animation and duration
    Does not store any image / surface information
    """
    DIRECTION_UP = "up"
    DIRECTION_RIGHT = "right"
    DIRECTION_LEFT = "left"
    DIRECTION_DOWN = "down"

    ANIM_IDLE = "idle"
    ANIM_MOVE = "move"
    ANIM_HIT = "hit"  #TODO: HIT IS NOT ANIMATION BUT EFFECT
    ANIM_DEATH = "death"
    ANIM_ATTACK = "attack"

    def __init__(self, sprite_list):
        self._duration = 0
        self._direction = Model.DIRECTION_UP
        self._animation = Model.ANIM_IDLE

        self._sprite_list = sprite_list

    def set_direction(self, angle):
        # Set direction of the model based on facing angle

        # Math to get facing:
        # vector angle is 0 (left) 90 (up) 180/-180 (right) -90 bottom
        # the direction is decided within 45 degrees of the number above
        # this value will obey the DIRECTION_XXX constant
        d = (int(math.ceil((angle + 135) / 90)) - 1) % 4

        if d == 0:   self._direction = Model.DIRECTION_UP
        elif d == 1: self._direction = Model.DIRECTION_RIGHT
        elif d == 2: self._direction = Model.DIRECTION_DOWN
        elif d == 3: self._direction = Model.DIRECTION_LEFT


    def set_animation(self, animation):
        self._animation = animation
        self._duration = 0

    def update(self, duration):
        self._duration += duration

    def render(self, target, position):
        # Step 1: Select the animation
        if self._animation in self._sprite_list:
            sprite_anim = self._sprite_list[self._animation]

        else:
            # For missing animation, use IDLE instead
            sprite_anim = self._sprite_list[Model.ANIM_IDLE]

        # Step 2: Select the direction
        if self._direction in sprite_anim:
            sprite = sprite_anim[self._direction]

        else:
            # For missing direction, use UP instead
            sprite = sprite_anim[Model.DIRECTION_UP]

        # Set the frame based on duration
        sprite.set_frame(self._duration)
        sprite.render(target, position)


class Sprite(object):
    def __init__(self):
        self.offset = (0, 0)
        self.surface = None

        # Data for animated sprite only
        self.surface_list = []
        self.frame = 0
        self.loop = None  # Loop Flag
        self.time = None  # Time per frame

    def set_frame(self, duration):
        # Only set frame for animated sprite
        if self.surface_list:
            frame = int(duration / self.time)

            if frame >= self.frame:
                if self.loop:
                    frame %= self.frame
                else:
                    frame = self.frame - 1


            self.surface = self.surface_list[frame]

    def render(self, target, position):
        (x, y) = position
        (dx, dy) = self.offset
        pos = (int(x + dx), int(y + dy))
        target.blit(self.surface, pos)

    def render_all_frame(self, target, position):
        """
        For debug purpose (Sprite Viewer) only
        """
        if self.surface_list:
            (x, y) = position
            for s in self.surface_list:
                target.blit(s, (x, y))
                x += s.get_size()[0]

# TODO: This thing should belong to resource manager i guess?
import json
import os

class SpriteLoader(object):
    def __init__(self, rs=None):
        if not rs:
            self.resource_manager = game.ResourceManager()
        else:
            self.resource_manager = rs

        self.model_sprite_list = {}

    def load_model(self, name):
        if name not in self.model_sprite_list:
            model_data_file = os.path.join('data', 'model', (name + ".json"))

            model_data = json.load(open(model_data_file, 'r'))

            sprite_list = {}
            for animation in model_data["sprite_list"]:
                model_animation = model_data["sprite_list"][animation]
                sprite_list[animation] = {}
                for direction in model_animation:
                    sprite = self.load_sprite_ex(model_animation[direction])
                    sprite_list[animation][direction] = sprite

            self.model_sprite_list[name] = sprite_list

        model = Model(self.model_sprite_list[name])
        return model


    def load_sprite_ex(self, sprite_data):
        image_filename = sprite_data["image_file"]
        master_image = self.resource_manager.get_image(image_filename)

        sprite = Sprite()
        sprite_size = sprite_data["size"]
        animated = sprite_data["animate"]
        sprite.offset = sprite_data["offset"]

        if animated:
            surface_list = []

            for p in sprite_data["frames"]:
                # UP
                image = master_image.subsurface((p, sprite_size))
                surface_list.append(image)


            sprite.surface_list = surface_list
            sprite.frame = len(surface_list)

            sprite.loop = sprite_data["loop"]
            sprite.time = sprite_data["time"]

        else:
            p = sprite_data["frames"]
            sprite.surface = master_image.subsurface((p, sprite_size))

        return sprite

    # TODO: Still need this file?
    def load_sprite(self, name):
        sprite_data_file = os.path.join('data', 'sprite', (name + ".json"))

        sprite_data = json.load(open(sprite_data_file, 'r'))

        return self.load_sprite_ex(sprite_data)

import pygame
from pygame.locals import K_ESCAPE
class SpriteViewer(object):


    def __init__(self):
        self.sprite = None

        self.pygame_run()

    def init(self):
        rs = game.ResourceManager()
        loader = SpriteLoader(rs)
        self.sprite = loader.load_sprite("baldric")


        model_name = "female"
        # Load a single model first to get the info
        model = loader.load_model(model_name)

        self.models = {}
        for animation in model._sprite_list:
            model_animation = model._sprite_list[animation]
            self.models[animation] = {}
            for direction in model_animation:
                m = loader.load_model(model_name)
                m.set_animation(animation)
                m._direction = direction
                self.models[animation][direction] = m

                # Force everything to loop
                model_animation[direction].loop = True

    def run(self, duration):
        screen = pygame.display.get_surface()

        """self.sprite.render_all_frame(screen, (0, 0))

        pygame.draw.line(screen, (255, 0, 0), (240, 320), (400, 320), 1)
        pygame.draw.line(screen, (255, 0, 0), (320, 240), (320, 400), 1)
        self.sprite.set_frame(duration)
        self.sprite.render(screen, (320, 320))"""

        #pygame.draw.line(screen, (255, 0, 0), (240, 320), (400, 320), 1)
        #pygame.draw.line(screen, (255, 0, 0), (320, 240), (320, 400), 1)

        font = pygame.font.Font(pygame.font.get_default_font(), 18)


        (x, y) = (150, 100)
        for a in self.models:
            surface = font.render(a, 1, (255, 255, 255))
            screen.blit(surface, (20, y - 20))
            for d in self.models[a]:
                model = self.models[a][d]
                model.update(duration)
                model.render(screen, (x, y))
                x += 64

            x = 150
            y += 64

    def pygame_run(self):
        pygame.init()
        pygame.font.init()
        pygame.display.set_caption('COS Sprite Viewer')
        pygame.display.set_mode((640, 480))
        clock = pygame.time.Clock()

        self.init()

        run = True
        while run:
            pygame.display.get_surface().fill((0, 0, 0))
            duration = clock.tick(30)
            for e in pygame.event.get():
                if e.type == pygame.QUIT:
                    run = False

                elif e.type == pygame.KEYDOWN:
                    if e.key == K_ESCAPE:
                        run = False

            self.run(duration)

            pygame.display.flip()

        pygame.quit()


if __name__ == "__main__":
    SpriteViewer()
